#include "res_xpt2046.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

/*当前触摸坐标*/
WHT_XPT2046_Pos_t WHT_XPT2046_Pos;


//速率建议最高
static volatile unsigned int* GPIO_CS;
static volatile unsigned int* GPIO_IRQ;
static WHT_Soft_SPI_Cache_t WHT_Cache;

static void GPIO_Init_Callback(void)
{
    WHT_GPIO_BSP.WHT_Set_Clock(SPI_CS_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(SPI_IRQ_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Register_Bit_Output(SPI_CS_Port, SPI_CS_Pin, &GPIO_CS);
    WHT_GPIO_BSP.WHT_Register_Bit_Input(SPI_IRQ_Port, SPI_IRQ_Pin, &GPIO_IRQ);
    *GPIO_CS = Hig;
    WHT_GPIO_BSP.WHT_Set_Mode(SPI_CS_Port, SPI_CS_Pin, Mode_Out_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(SPI_IRQ_Port, SPI_IRQ_Pin, Mode_IPU);
}
static void Set_GPIO_CS_State_Callback(WHT_Sort_SPI_BUS_IO_State_enum state)
{
    *GPIO_CS = (WHT_GPIO_State_enum)state;
}


static void WHT_Soft_SPI_Init(void)
{
    if (WHT_Soft_SPI_BUS1_Init() != 0)
    {
        /* 总线初始化失败 */
        return;
    }
	WHT_Cache.Dir = Soft_SPI_RW;
    WHT_Cache.Set_NSS_State_CB = Set_GPIO_CS_State_Callback;
}
static void WHT_SPI_Full_Duplex(uint8_t* tx_buffer, uint8_t* rx_buffer, uint8_t length)
{
	GPIO_Init_Callback();
    while (WHT_Soft_SPI_BUS1->Mutex == Soft_SPI_Lock)
    {
        //vtaskdelay(1);
    }
	WHT_Cache.Buffer_Count = length;
    WHT_Cache.Tx_Buffer = tx_buffer;
    WHT_Cache.Rx_Buffer = rx_buffer;
	WHT_Soft_SPI_BUS_OPS.Read_Write(WHT_Soft_SPI_BUS1, &WHT_Cache);
}
/*读取 XPT2046 的X通道和Y通道的AD值（12 bit，最大是4096）*/
static void WHT_XPT2046_Read_ADC_XY(uint16_t* x_adc, uint16_t* y_adc)
{
	typedef struct
	{
		uint32_t adc:12;
	}WHT_XPT2046_Adc_Value_t;
	uint8_t Tx_Cmd[4];
	uint8_t Rx_Adc[4];

	*(uint32_t*)Tx_Cmd = XPT2046_CHANNEL_X << (8+5);
    WHT_SPI_Full_Duplex(Tx_Cmd, Rx_Adc, 1);//21bit
	*x_adc = ((WHT_XPT2046_Adc_Value_t*)Rx_Adc)->adc;

	*(uint32_t*)Tx_Cmd = XPT2046_CHANNEL_Y << (8+5);
    WHT_SPI_Full_Duplex(Tx_Cmd, Rx_Adc, 1);//21bit
    *y_adc = ((WHT_XPT2046_Adc_Value_t*)Rx_Adc)->adc;
}

void WHT_XPT2046_Init(void)
{
	WHT_XPT2046_Pos.New_X = 0;
	WHT_XPT2046_Pos.New_Y = 0;
	WHT_XPT2046_Pos.State = false;
	WHT_Soft_SPI_Init();
}


/*在触摸 XPT2046 屏幕时获取一组坐标的AD值，并对该坐标进行滤波,不是很精准，但是简单，速度比较快*/
static bool WHT_XPT2046_Get_Smooth_ADC_XY_Qucik(uint16_t* x_adc_filter, uint16_t* y_adc_filter)
{
	#define Get_Count   10

	uint16_t Buffer[2][Get_Count] = {0};  //坐标X和Y进行多次采样
	//存储采样中的最小值、最大值
	uint32_t Min_X = 0;
    uint32_t Max_X = 0;
    uint32_t Min_Y = 0;
    uint32_t Max_Y = 0;

	/* 循环采样 */
    for (uint8_t i = 0; i < Get_Count; i++)
    {
        WHT_XPT2046_Read_ADC_XY(&Buffer[0][i], &Buffer[1][i]);

        /*获取x轴最大值和最小值*/
		if (Buffer[0][i] < Min_X)
			Min_X = Buffer[0][i];
		else if (Buffer[0][i] > Max_X)
			Max_X = Buffer[0][i];
        /*获取y轴最大值和最小值*/
		if (Buffer[1][i] < Min_Y )
			Min_Y = Buffer[1][i];
		else if (Buffer[1][i] > Max_Y)
			Max_Y = Buffer[1][i];
		
		if (*GPIO_IRQ != XPT2046_IRQ_Enable)
			return false;
    }
	/*如果成功采样*/
	uint32_t Sum_X = 0;
	uint32_t Sum_Y = 0;
	for (uint8_t i = 0; i < Get_Count; i++)
	{
		Sum_X += Buffer[0][i];
		Sum_Y += Buffer[1][i];
	}
	/*去除最小值和最大值之后求平均值*/
	*x_adc_filter = (Sum_X - Min_X - Max_X) >> 3;
	*y_adc_filter = (Sum_Y - Min_Y - Max_Y) >> 3;
    return true;	
}

/*获取 XPT2046 触摸点（校准后）的坐标*/
static bool WHT_XPT2046_Get_Touch_Pos(WHT_XPT2046_Pos_t* pos, const XPT2046_Touch_Parameters_t* touch_par)
{
	uint16_t X_ADC_Value,Y_ADC_Value;

  	if (WHT_XPT2046_Get_Smooth_ADC_XY_Qucik(&X_ADC_Value, &Y_ADC_Value) == true)
  	{
		pos->New_X = touch_par->dX_X * X_ADC_Value + touch_par->dX_Y * Y_ADC_Value + touch_par->dX;        
		pos->New_Y = touch_par->dY_X * X_ADC_Value + touch_par->dY_Y * Y_ADC_Value + touch_par->dY;
		return true;
	}
  	return false;
} 



//默认触摸参数，不同的屏幕稍有差异，可重新调用触摸校准函数获取
static XPT2046_Touch_Parameters_t XPT2046_Touch_Parameters[] =
{ 	
    -0.006464f, -0.073259f, 280.358032f,  0.074878f,  0.002052f,  -6.545977f,//扫描方式0
     0.086314f,  0.001891f, -12.836658f, -0.003722f, -0.065799f, 254.715714f,//扫描方式1
	 0.002782f,  0.061522f, -11.595689f,  0.083393f,  0.005159f, -15.650089f,//扫描方式2
	 0.089743f, -0.000289f, -20.612209f, -0.001374f,  0.064451f, -16.054003f,//扫描方式3
	 0.000767f, -0.068258f, 250.891769f, -0.085559f, -0.000195f, 334.747650f,//扫描方式4
    -0.084744f,  0.000047f, 323.163147f, -0.002109f, -0.066371f, 260.985809f,//扫描方式5
    -0.001848f,  0.066984f, -12.807136f, -0.084858f, -0.000805f, 333.395386f,//扫描方式6
    -0.085470f, -0.000876f, 334.023163f, -0.003390f,  0.064725f,  -6.211169f,//扫描方式7
};

/*触摸屏检测状态机*/
/*true :触摸按下  false :无触摸*/
static bool XPT2046_Touch_Detect(void)
{
	static Touch_State_enum Touch_State = XPT2046_STATE_RELEASE;
	static uint32_t Count;
	bool Result = false;
	
	switch(Touch_State)
	{
	case XPT2046_STATE_RELEASE:
		Touch_State = *GPIO_IRQ == XPT2046_IRQ_Enable ? XPT2046_STATE_WAITING : XPT2046_STATE_RELEASE;
		Count = 0;
		break;
	case XPT2046_STATE_WAITING:
		if(*GPIO_IRQ == XPT2046_IRQ_Enable)
		{
			//等待时间大于阈值则认为触摸被按下
			//消抖时间 = Touch_Filter_Count * 本函数被调用的时间间隔
			//如在定时器中调用，每10ms调用一次，则消抖时间为：DURIATION_TIME*10ms
			if(Count++ > Touch_Filter_Count)
			{
				Count = 0;
				Touch_State = XPT2046_STATE_PRESSED;
				Result = true;
			}
		}
		else	//等待时间值未达到阈值就为无效电平，当成抖动处理
       		Touch_State = XPT2046_STATE_RELEASE; 
		break;
	case XPT2046_STATE_PRESSED:
		if(*GPIO_IRQ == XPT2046_IRQ_Enable)//触摸持续按下
		{
			Touch_State = XPT2046_STATE_PRESSED;
			Result = true;
		}
		else	//触摸释放
			Touch_State = XPT2046_STATE_RELEASE;
		break;
	default:
		Touch_State = XPT2046_STATE_RELEASE;
		break;
	}
	return Result;
}

/*
检测到触摸中断时调用的处理函数,通过它调用tp_down 和tp_up汇报触摸点
本函数需要在while循环里被调用，也可使用定时器定时调用
例如，可以每隔5ms调用一次，消抖阈值宏DURIATION_TIME可设置为2，这样每秒最多可以检测100个点。
可在XPT2046_TouchDown及XPT2046_TouchUp函数中编写自己的触摸应用
display_mode 的值在枚举它里 WHT_MCU_Display_Mode_enum
*/
void WHT_XPT2046_Touch_Even_Handler(uint8_t display_mode)
{
	if(XPT2046_Touch_Detect() == true)
	{
		//获取触摸坐标
		WHT_XPT2046_Get_Touch_Pos(&WHT_XPT2046_Pos, &XPT2046_Touch_Parameters[display_mode]);
		/*更新触摸状态*/
		WHT_XPT2046_Pos.State = true;
	}
	else
	{
		WHT_XPT2046_Pos.State = false;
	}
}




/*以下是触摸校准的 以后在整理*/

typedef struct         //校准因子结构体 
{
	float An;//注:sizeof(long double) = 8
	float Bn;
	float Cn;
	float Dn;
	float En;
	float Fn;
	float Divider;
}XPT2046_Calibration_t;


#if 0
/**
  * @brief  计算 XPT2046 触摸坐标校正系数（注意：只有在LCD和触摸屏间的误差角度非常小时,才能运用下面公式）
  * @param  pDisplayCoordinate ：屏幕人为显示的已知坐标
  * @param  pstrScreenSample ：对已知坐标点触摸时 XPT2046 产生的坐标
  * @param  pCalibrationFactor ：根据人为设定坐标和采样回来的坐标计算出来的屏幕触摸校正系数
  * @retval 计算状态
	*   该返回值为以下值之一：
  *     @arg 1 :计算成功
  *     @arg 0 :计算失败
  */
static uint8_t XPT2046_Calculate_CalibrationFactor (WHT_XPT2046_Pos_t* pDisplayCoordinate, WHT_XPT2046_Pos_t* pScreenSample, XPT2046_Calibration_t* pCalibrationFactor )
{
	/* K＝ ( X0－X2 )  ( Y1－Y2 )－ ( X1－X2 )  ( Y0－Y2 ) */
	pCalibrationFactor->Divider = ((pScreenSample[0].New_X - pScreenSample[2].New_X) * (pScreenSample[1].New_Y - pScreenSample[2].New_Y)) - 
								  ((pScreenSample[1].New_X - pScreenSample[2].New_X) * (pScreenSample[0].New_Y - pScreenSample[2].New_Y));
	if (pCalibrationFactor->Divider == 0)
		return 0;
	
	/* A＝ (  ( XD0－XD2 )  ( Y1－Y2 )－ ( XD1－XD2 )  ( Y0－Y2 ) )／K	*/
	pCalibrationFactor->An = ((pDisplayCoordinate[0].New_X - pDisplayCoordinate[2].New_X) * (pScreenSample[1].New_Y - pScreenSample[2].New_Y)) - 
							 ((pDisplayCoordinate[1].New_X - pDisplayCoordinate[2].New_X) * (pScreenSample[0].New_Y - pScreenSample[2].New_Y));
		
	/* B＝ (  ( X0－X2 )  ( XD1－XD2 )－ ( XD0－XD2 )  ( X1－X2 ) )／K	*/
	pCalibrationFactor->Bn = ((pScreenSample[0].New_X - pScreenSample[2].New_X) * (pDisplayCoordinate[1].New_X - pDisplayCoordinate[2].New_X)) - 
			                 ((pDisplayCoordinate[0].New_X - pDisplayCoordinate[2].New_X) * (pScreenSample[1].New_X - pScreenSample[2].New_X));
		
	/* C＝ ( Y0 ( X2XD1－X1XD2 )+Y1 ( X0XD2－X2XD0 )+Y2 ( X1XD0－X0XD1 ) )／K */
	pCalibrationFactor->Cn = (pScreenSample[2].New_X * pDisplayCoordinate[1].New_X - pScreenSample[1].New_X * pDisplayCoordinate[2].New_X) * pScreenSample[0].New_Y +
				             (pScreenSample[0].New_X * pDisplayCoordinate[2].New_X - pScreenSample[2].New_X * pDisplayCoordinate[0].New_X) * pScreenSample[1].New_Y +
			                 (pScreenSample[1].New_X * pDisplayCoordinate[0].New_X - pScreenSample[0].New_X * pDisplayCoordinate[1].New_X) * pScreenSample[2].New_Y ;
		
	/* D＝ (  ( YD0－YD2 )  ( Y1－Y2 )－ ( YD1－YD2 )  ( Y0－Y2 ) )／K	*/
	pCalibrationFactor->Dn = ((pDisplayCoordinate[0].New_Y - pDisplayCoordinate[2].New_Y) * (pScreenSample[1].New_Y - pScreenSample[2].New_Y)) - 
			                 ((pDisplayCoordinate[1].New_Y - pDisplayCoordinate[2].New_Y) * (pScreenSample[0].New_Y - pScreenSample[2].New_Y)) ;
		
	/* E＝ (  ( X0－X2 )  ( YD1－YD2 )－ ( YD0－YD2 )  ( X1－X2 ) )／K	*/
	pCalibrationFactor->En = ((pScreenSample[0].New_X - pScreenSample[2].New_X) * ( pDisplayCoordinate[1].New_Y - pDisplayCoordinate[2].New_Y)) - 
			                 ((pDisplayCoordinate[0].New_Y - pDisplayCoordinate[2].New_Y) * (pScreenSample[1].New_X - pScreenSample[2].New_X));
		
	/* F＝ ( Y0 ( X2YD1－X1YD2 )+Y1 ( X0YD2－X2YD0 )+Y2 ( X1YD0－X0YD1 ) )／K */
	pCalibrationFactor->Fn = (pScreenSample[2].New_X * pDisplayCoordinate[1].New_Y - pScreenSample[1].New_X * pDisplayCoordinate[2].New_Y) * pScreenSample[0].New_Y +
							 (pScreenSample[0].New_X * pDisplayCoordinate[2].New_Y - pScreenSample[2].New_X * pDisplayCoordinate[0].New_Y) * pScreenSample[1].New_Y +
						     (pScreenSample[1].New_X * pDisplayCoordinate[0].New_Y - pScreenSample[0].New_X * pDisplayCoordinate[1].New_Y) * pScreenSample[2].New_Y;
	return 1;
}

/**
  * @brief  XPT2046 触摸屏校准
	* @param	LCD_Mode：指定要校正哪种液晶扫描模式的参数
  * @note  本函数调用后会把液晶模式设置为LCD_Mode
  * @retval 校准结果
	*   该返回值为以下值之一：
  *     @arg 1 :校准成功
  *     @arg 0 :校准失败
  */
uint8_t WHT_XPT2046_Touch_Calibrate(uint8_t display_mode) 
{
	uint8_t i;		
	char cStr[100];	
	uint16_t usTest_x = 0, usTest_y = 0, usGap_x = 0, usGap_y = 0;	
	char * pStr = 0;
	
    WHT_XPT2046_Pos_t Test_Pos[4], Screen_Pos[4];
	XPT2046_Calibration_t CalibrationFactor;
    
	//设置扫描方向，横屏
	ILI9341_GramScan (display_mode);
		
		
	/* 设定“十”字交叉点的坐标 */ 
	Test_Pos[0].New_X = LCD_X_LENGTH >> 2;
	Test_Pos[0].New_Y = LCD_Y_LENGTH >> 2;
		
	Test_Pos[1].New_X = Test_Pos[0].New_X;
	Test_Pos[1].New_Y = ( LCD_Y_LENGTH * 3 ) >> 2;
		
	Test_Pos[2].New_X = ( LCD_X_LENGTH * 3 ) >> 2;
	Test_Pos[2].New_Y = Test_Pos[1].New_Y;
		
	Test_Pos[3].New_X = Test_Pos[2].New_X;
	Test_Pos[3].New_Y = Test_Pos[0].New_Y;	
		
		
	for ( i = 0; i < 4; i ++ )
	{ 
		ILI9341_Clear ( 0, 0, LCD_X_LENGTH, LCD_Y_LENGTH );       
			
		pStr = "Touch Calibrate ......";		
		//插入空格，居中显示
		sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFONT *)LCD_GetFont())->Width))-strlen(pStr))/2,' ',pStr)	;	
        ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );			
		
		//插入空格，居中显示
		sprintf ( cStr, "%*c%d",((LCD_X_LENGTH/(((sFONT *)LCD_GetFont())->Width)) -1)/2,' ',i + 1 );
		ILI9341_DispStringLine_EN (( LCD_Y_LENGTH >> 1 ) - (((sFONT *)LCD_GetFont())->Height), cStr ); 
		XPT2046_DelayUS ( 300000 );		                     //适当的延时很有必要
		ILI9341_DrawCross (Test_Pos[i] .New_X, Test_Pos[i].New_Y );  //显示校正用的“十”字
		while (!WHT_XPT2046_Get_Smooth_ADC_XY_Qucik(&Screen_Pos[i].New_X, &Screen_Pos[i].New_Y));    //读取XPT2046数据到变量pCoordinate，当ptr为空时表示没有触点被按下
	}
		
	XPT2046_Calculate_CalibrationFactor (Test_Pos, Screen_Pos, &CalibrationFactor);  	 //用原始参数计算出 原始参数与坐标的转换系数
	if ( CalibrationFactor.Divider == 0 ) goto Failure;
	usTest_x = ((CalibrationFactor.An * Screen_Pos[3].New_X ) + (CalibrationFactor.Bn * Screen_Pos[3].New_Y ) + CalibrationFactor.Cn ) / CalibrationFactor.Divider;		//取一个点计算X值	 
	usTest_y = ((CalibrationFactor.Dn * Screen_Pos[3].New_X ) + (CalibrationFactor.En * Screen_Pos[3].New_Y ) + CalibrationFactor.Fn ) / CalibrationFactor.Divider;    //取一个点计算Y值
		
	usGap_x = (usTest_x > Test_Pos[3].New_X) ? (usTest_x - Test_Pos[3].New_X) : (Test_Pos[3].New_X - usTest_x);   //实际X坐标与计算坐标的绝对差
	usGap_y = (usTest_y > Test_Pos[3].New_Y) ? (usTest_y - Test_Pos[3].New_Y) : (Test_Pos[3].New_Y - usTest_y);   //实际Y坐标与计算坐标的绝对差
	
    if ((usGap_x > 15) || (usGap_y > 15)) goto Failure;       //可以通过修改这两个值的大小来调整精度    
	
    /* 校准系数为全局变量 */ 
	XPT2046_Touch_Parameters[display_mode].dX_X = (CalibrationFactor.An * 1.0f) / CalibrationFactor.Divider;
	XPT2046_Touch_Parameters[display_mode].dX_Y = (CalibrationFactor.Bn * 1.0f) / CalibrationFactor.Divider;
	XPT2046_Touch_Parameters[display_mode].dX   = (CalibrationFactor.Cn * 1.0f) / CalibrationFactor.Divider;
		
	XPT2046_Touch_Parameters[display_mode].dY_X = (CalibrationFactor.Dn * 1.0f) / CalibrationFactor.Divider;
	XPT2046_Touch_Parameters[display_mode].dY_Y = (CalibrationFactor.En * 1.0f) / CalibrationFactor.Divider;
	XPT2046_Touch_Parameters[display_mode].dY   = (CalibrationFactor.Fn * 1.0f) / CalibrationFactor.Divider;
		
	ILI9341_Clear(0, 0, LCD_X_LENGTH, LCD_Y_LENGTH );
	
	pStr = "Calibrate Succed";
	//插入空格，居中显示	
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFONT *)LCD_GetFont())->Width))-strlen(pStr))/2,' ',pStr);
  	ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );

	return 1;
Failure:
	ILI9341_Clear ( 0, 0, LCD_X_LENGTH, LCD_Y_LENGTH );
	
	pStr = "Calibrate fail";
	//插入空格，居中显示	
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFONT *)LCD_GetFont())->Width))-strlen(pStr))/2,' ',pStr);
  	ILI9341_DispStringLine_EN (LCD_Y_LENGTH >> 1, cStr );	

	pStr = "try again";
	//插入空格，居中显示
	sprintf(cStr,"%*c%s",((LCD_X_LENGTH/(((sFONT *)LCD_GetFont())->Width))-strlen(pStr))/2,' ',pStr);
	ILI9341_DispStringLine_EN ( ( LCD_Y_LENGTH >> 1 ) + (((sFONT *)LCD_GetFont())->Height), cStr );
	return 0; 
}
#endif
